<script>
  Polymer({
    is: 'x-basic',
    properties: {
      notifyingValue: {
        type: Number,
        notify: true
      }
    }
  });
</script>

<template>
  <x-basic id="basic1" notifying-value="{{obj.value}}" attrvalue$="{{obj.value}}" othervalue="{{obj.value2}}"></x-basic>
  <x-basic id="basic2" notifying-value="{{obj.value}}" attrvalue$="{{obj.value}}"></x-basic>
  <x-basic id="basic3" notifying-value="{{!obj.value}}" attrvalue$="{{!obj.value}}"></x-basic>
</template>
<script>
  Polymer({
    is: 'x-compose',
    properties: {
      obj: {
        type: Object,
        notify: true
      }
    },
    observers: [
      'objSubpathChanged(obj.*)',
      'objValueChanged(obj.value)',
    ],
    created: function() {
      this.observerCounts = {
        objSubpathChanged: 0,
        objValueChanged: 0
      };
    },
    clearObserverCounts: function() {
      for (var i in this.observerCounts) {
        this.observerCounts[i] = 0;
      }
    },
    objSubpathChanged: function(change) {
      this.observerCounts.objSubpathChanged++;
      assert.equal(change.base, this.obj);
      if (this.expectedObjSubpath) {
        assert.equal(change.path, this.expectedObjSubpath);
        assert.equal(change.value, this.expectedObjValue);
      }
    },
    objValueChanged: function(value) {
      this.observerCounts.objValueChanged++;
      assert.equal(this.obj.value, value);
    },
  });
</script>

<template>
  <x-compose id="compose" obj="{{obj}}"></x-compose>
</template>
<script>
  Polymer({
    is: 'x-forward',
    properties: {
      obj: {
        type: Object,
        notify: true
      }
    },
    observers: [
      'objSubpathChanged(obj.*)',
      'objValueChanged(obj.value)',
    ],
    created: function() {
      this.observerCounts = {
        objSubpathChanged: 0,
        objValueChanged: 0
      };
    },
    clearObserverCounts: function() {
      for (var i in this.observerCounts) {
        this.observerCounts[i] = 0;
      }
      this.$.compose.clearObserverCounts();
    },
    objSubpathChanged: function(change) {
      this.observerCounts.objSubpathChanged++;
      assert.equal(change.base, this.obj);
      if (this.expectedObjSubpath) {
        assert.equal(change.path, this.expectedObjSubpath);
        assert.equal(change.value, this.expectedObjValue);
      }
    },
    objValueChanged: function(value) {
      this.observerCounts.objValueChanged++;
      assert.equal(this.obj.value, value);
    },
  });
</script>

<template>
  <x-basic id="basic" notifying-value="{{nested.obj.value}}" attrvalue$="{{nested.obj.value}}"></x-basic>
  <x-compose id="compose" obj="{{nested.obj}}"></x-compose>
  <x-forward id="forward" obj="{{nested.obj}}"></x-forward>
  <div id="boundChild" computed-from-paths="{{computeFromPaths(a, nested.b, nested.obj.c)}}" array-length="{{data.length}}"></div>
</template>
<script>
  Polymer({
    is: 'x-stuff',
    properties: {
      computedFromPaths: {
        computed: 'computeFromPaths(a, nested.b, nested.obj.c)'
      }
    },
    observers: [
      'nestedSubpathChanged(nested.*)',
      'nestedObjChanged(nested.obj)',
      'nestedObjSubpathChanged(nested.obj.*)',
      'nestedObjValueChanged(nested.obj.value)',
      'multipleChanged(a, b, nested.obj.*)',
      'multiplePathsChanged(a, nested.b, nested.obj.c)',
      'arrayChanged(array.splices)',
      'arrayOrPropChanged(prop, array.splices)'
    ],
    created: function() {
      this.observerCounts = {
        nestedSubpathChanged: 0,
        nestedObjChanged: 0,
        nestedObjSubpathChanged: 0,
        nestedObjValueChanged: 0,
        multipleChanged: 0,
        multiplePathsChanged: 0,
        arrayChanged: 0,
        arrayOrPropChanged: 0
      };
    },
    clearObserverCounts: function() {
      for (var i in this.observerCounts) {
        this.observerCounts[i] = 0;
      }
      this.$.compose.clearObserverCounts();
      this.$.forward.clearObserverCounts();
    },
    nestedSubpathChanged: function(change) {
      this.observerCounts.nestedSubpathChanged++;
      assert.equal(change.base, this.nested);
      if (this.expectedNestedSubpath) {
        assert.equal(change.path, this.expectedNestedSubpath);
        assert.equal(change.value, this.expectedNestedValue);
      }
    },
    nestedObjChanged: function(value) {
      this.observerCounts.nestedObjChanged++;
      assert.equal(this.nested.obj, value);
    },
    nestedObjSubpathChanged: function(change) {
      this.observerCounts.nestedObjSubpathChanged++;
      assert.equal(change.base, this.nested.obj);
      if (this.expectedNestedObjSubpath) {
        assert.equal(change.path, this.expectedNestedObjSubpath);
        assert.equal(change.value, this.expectedNestedObjValue);
      }
    },
    nestedObjValueChanged: function(value) {
      this.observerCounts.nestedObjValueChanged++;
      assert.equal(this.get('nested.obj.value'), value);
    },
    multipleChanged: function(a, b, nestedObjChange) {
      this.observerCounts.multipleChanged++;
      assert.equal(a, 'a');
      assert.equal(b, 'b');
      assert.equal(nestedObjChange.base, this.nested.obj);
      if (this.expectedNestedObjSubpath) {
        assert.equal(nestedObjChange.path, this.expectedNestedObjSubpath);
        assert.equal(nestedObjChange.value, this.expectedNestedObjValue);
      }
      assert.equal(nestedObjChange.base, this.nested.obj);
    },
    computeFromPaths: function(a, b, c) {
      assert.equal(a, this.a, 'computeFromNested `a` arg incorrect');
      assert.equal(b, this.nested.b, 'computeFromNested `b` arg incorrect');
      assert.equal(c, this.nested.obj.c, 'computeFromNested `c` arg incorrect');
      return a + b + c;
    },
    multiplePathsChanged: function(a, b, c) {
      this.observerCounts.multiplePathsChanged++;
      assert.equal(a, this.a, 'multiplePathsChanged `a` arg incorrect');
      assert.equal(b, this.nested.b, 'multiplePathsChanged `b` arg incorrect');
      assert.equal(c, this.nested.obj.c, 'multiplePathsChanged `c` arg incorrect');
    },
    arrayChanged: function(splices) {
      this.observerCounts.arrayChanged++;
      if (this.array.length) {
        assert.equal(splices.indexSplices.length, 1,
          'arrayOrPropChanged indexSplices incorrect');
        assert.equal(splices.indexSplices[0].addedCount, 3,
          'arrayOrPropChanged indexSplices incorrect');
        assert.equal(splices.keySplices.length, 1,
          'arrayOrPropChanged keySplices incorrect');
        assert.equal(splices.keySplices[0].added.length, 3,
          'arrayOrPropChanged indexSplices incorrect');
      }
    },
    arrayOrPropChanged: function(prop, splices) {
      this.observerCounts.arrayOrPropChanged++;
      assert.equal(prop, this.prop);
    }
  });
</script>
